#include "lib.h"

#include "shue.h"

#include "wargame.h"
#include "warconn.h"
#include "warhand.h"
#include "cardmgr.h"
#include "warsaved.h"

#define WAR_NUMDECKS	1		// num decks in shue

enum Outcomes
{
	O_PLAYER_FOLD,
	O_DEALER_WON,
	O_PLAYER_WON,
	O_PUSH
};

static CardMgr stdMgr;


WarGame::WarGame(WarConnection* conn, int32 bet)
{
	this->conn = conn;
	this->bet = bet;

	dealer = new WarHand(&stdMgr);
	player = new WarHand(&stdMgr);
	shue = (Shue*)0;
}

WarGame::WarGame(WarConnection* conn, WarSavedGame* saved)
{
	this->conn = conn;

	// everything else is restored in restore()
	//
	bet = 0;
	dealer = player = (WarHand*)0;
	shue = (Shue*)0;
		
	restore(saved);
}

WarGame::~WarGame()
{
	delete dealer;
	delete player;
	delete shue;
}

bool8
WarGame::deal(int32 balance)
{
	bool8 result = false;

	ShueGameInfo sgi;
	sgi.gameId = GAMEID;
	sgi.machId = MACHID;
     #ifdef FORCEDSHOE 
       sgi.pid=conn->GetPID();
    #endif


	shue = new Shue(&stdMgr, WAR_NUMDECKS, sgi);

	if (shue && dealer && player)
	{
		shue->shuffle();

		player->add(shue->getCard());
		dealer->add(shue->getCard());

		if (conn->trackDeal(dealer, player))
		{
			
			// if there was a push, send the info to engage war
			if (stdMgr.getRank(dealer->getCardAt(0)) == 
				stdMgr.getRank(player->getCardAt(0))
				)
			{
				conn->trackSaveGame();

				conn->msgStart();
					conn->msgAdd((uint8)'D');

					conn->msgReleaseBuf(dealer->outfirst(conn->msgGetBuf()));
					conn->msgReleaseBuf(player->outfirst(conn->msgGetBuf()));

					conn->msgAdd(balance);
				conn->msgEnd();

				result = true;
		
			}
			// otherwise, the game is over
			else
			{
				result = noWar(balance);	// "this" is deleted if successful
			}
		}	

	}
	else
	{
		delete shue;
		shue = (Shue*)0;
	}

	return result;
}

bool8
WarGame::raise(int32 balance)
{
	bool8 result = false;
	int32 i;

	if (shue && dealer && player)
	{
		// burn three cards
		for (i=0; i<3; i++)
		{
			shue->getCard();
		}
		player->add( shue->getCard() );
		
		// burn three cards
		for (i=0; i<3; i++)
		{
			shue->getCard();
		}
		dealer->add( shue->getCard() );

		if (conn->trackRaise(dealer, player))
			result = Finished(true, 1, balance);	// "this" deleted if success
		else
			shue->pushBackNCards(8);
	}

	return result;
}

bool8
WarGame::fold(int32 balance)
{
	bool8 result = false;

	if (shue && dealer && player)
		result = Finished(false, 0, balance);		// "this" deleted if success

	return result;
}


bool8
WarGame::Finished(bool8 raised, int32 cardIndex, int32 balance)
{
	int32 payouts[2], balAdjust;
	int32 outcome;
	
	balAdjust = calcPayouts(payouts, raised, !raised, cardIndex, &outcome);

	// trackGameDone() is expecting a true value for noWar
	// if there was no war, but obviously there was!
	if (conn->trackGameDone(raised, false, payouts[0], payouts[1], outcome,
		balAdjust, &balance))
	{

		conn->trackRemoveSavedGame();

		conn->msgStart();

		if (raised)
		{
			conn->msgAdd((uint8)'R');
		}
		else
		{
			conn->msgAdd((uint8)'F');
		}
		
		conn->msgReleaseBuf(dealer->outCard(cardIndex, conn->msgGetBuf()));
		conn->msgReleaseBuf(player->outCard(cardIndex, conn->msgGetBuf()));

		conn->msgAdd(payouts[0]);
		conn->msgAdd(payouts[1]);

		conn->msgAdd((uint8)(outcome & 0xff));
		
		conn->msgAdd(balance);
		conn->msgEnd();

		conn->gameDone();	// delete "this"

		return true;
	}

	return false;
}


bool8
WarGame::noWar(int32 balance)
{
	int32 payouts[2], balAdjust;
	int32 outcome;
	
	balAdjust = calcPayouts(payouts, false, false, 0, &outcome);

	if (conn->trackGameDone(false, true, payouts[0], payouts[1], outcome,
		balAdjust, &balance))
	{
		conn->msgStart();
		conn->msgAdd((uint8)'X');
		
		conn->msgReleaseBuf(dealer->outfirst(conn->msgGetBuf()));
		conn->msgReleaseBuf(player->outfirst(conn->msgGetBuf()));

		conn->msgAdd(payouts[0]);
		conn->msgAdd(payouts[1]);

		conn->msgAdd((uint8)(outcome & 0xff));
		
		conn->msgAdd(balance);
		conn->msgEnd();

		conn->gameDone();	// delete "this"

		return true;
	}

	return false;
}


int32
WarGame::calcOutcome(int32 cardIndex)
{
	int32 dealerRank, playerRank;

	dealerRank = stdMgr.getRank(dealer->getCardAt(cardIndex));
	playerRank = stdMgr.getRank(player->getCardAt(cardIndex));
		
	if (dealerRank > playerRank)
		return O_DEALER_WON;
	else if (dealerRank < playerRank)
		return O_PLAYER_WON;
	else
		return O_PUSH;
}

int32
WarGame::calcPayouts(int32 *payouts, bool8 raised, bool8 folded,
	int32 cardIndex, int32* outcome)
{
	int32 winnings = 0;
	

	*outcome = calcOutcome(cardIndex);

	if (raised)
	{
		switch (*outcome)
		{
			// Raised and Dealer Won
			case O_DEALER_WON:
				payouts[0] = -bet;
				payouts[1] = -bet;
				winnings = 0;
				break;

			// Raised and Player Won
			case O_PLAYER_WON:
				payouts[0] = bet;
				payouts[1] = 0;
				// if the player wins a war, they get
				// their bet and raise back, plus payout equal
				// to their initial bet
				winnings = bet + payouts[0] + bet;
				break;

			// house gives even odds for pushed raise
			case O_PUSH:
				payouts[0] = bet;
				payouts[1] = 0;
				// if the player wins a war, they get
				// their bet and raise back, plus even payout
				// to their initial bet
				winnings = bet + payouts[0] + bet;
				break;

		}
	}
	else
	{
		if (folded)
		{
			payouts[0] = -bet;
			payouts[1] = 0;
			winnings = 0;
			// set the outcome to reflect actual outcome
			*outcome = O_PLAYER_FOLD;
		}
		else
		{
			switch (*outcome)
			{
				case O_DEALER_WON:
					payouts[0] = -bet;
					payouts[1] = 0;
					winnings = 0;
					break;

				case O_PLAYER_WON:
				// house is giving the push away
				case O_PUSH:
					payouts[0] = bet;
					payouts[1] = 0;
					// if the player wins straight up (no war)
					// they get back their bet plus payout equal
					// to their initial bet
					winnings = bet + payouts[0];
					break;

			}
		}
		
	}
	
	return winnings;
}

void
WarGame::save(WarSavedGame* saved)
{
	shue->save(&saved->shue);
	// everything is already stored in the database
	//
}

void
WarGame::restore(WarSavedGame* saved)
{
	bet = saved->bet;

	ShueGameInfo sgi;
	sgi.gameId = GAMEID;
	sgi.machId = MACHID;
     #ifdef FORCEDSHOE 
       sgi.pid=conn->GetPID();
    #endif


	shue = new Shue(&stdMgr, WAR_NUMDECKS, sgi);
	shue->restore(&saved->shue);

	dealer = new WarHand(&stdMgr);
	dealer->restore(saved->dealerCards, saved->numDealerCards);

	player = new WarHand(&stdMgr);
	player->restore(saved->playerCards, saved->numPlayerCards);
	
	//
	// At this point the server is *exactly* the same as when the game was
	// saved:  all automatic playing had already completed and the game
	// was idle.
	//
}

void
WarGame::send()
{
	//
	// This message is contained within another message
	// (don't call msgStart()/msgEnd()).
	//
	conn->msgAdd(bet);
	conn->msgReleaseBuf(dealer->out(conn->msgGetBuf()));
	conn->msgReleaseBuf(player->out(conn->msgGetBuf()));
}
